#include "CelSphere.h"

CelBackground::CelBackground() : D3D11Effect() {
	tbuf = TileManager::tbuf;
	TPL = TileManager::TPL;
	NLAT = TileManager::NLAT;
	NLNG = TileManager::NLNG;
	patchidx = TileManager::patchidx;
	cfg = TileManager::cfg;

	InitCelBackgroundEffect();

	char *tmp = (char*)gc->GetConfigParam( CFGPRM_CSPHERETEXTURE );
	if( !tmp[0] ) {
		disabled = true;
		return;
	}
	else {
		disabled = false;
		strcpy( texname, tmp );
	}

	Intensity = (float)( *(double*)gc->GetConfigParam( CFGPRM_CSPHEREINTENS ) );

	maxlvl = 8;
	maxbaselvl = 8;
	int maxidx = patchidx[maxbaselvl];
	tiledesc = new TILEDESC [maxidx];
	memset( tiledesc, 0, sizeof(TILEDESC)*maxidx );

	LoadTextures();

	MATRIX3 R = {	2000, 0, 0,
					0, 2000, 0,
					0, 0, 2000	};

	double theta = 60.18*RAD;
	double phi = 90.02*RAD;
	double lambda = 173.6*RAD;
	double sint = sin( theta ), cost = cos( theta );
	double sinp = sin( phi ), cosp = cos( phi );
	double sinl = sin( lambda ), cosl = cos( lambda );

	ecl2gal = _M(	cosp, 0, sinp,
					0, 1, 0,
					-sinp, 0, cosp	);

	ecl2gal = mul( _M(	1, 0, 0,
						0, cost, sint,
						0, -sint, cost ), ecl2gal );

	ecl2gal = mul( _M(	cosl, 0, sinl,
						0, 1, 0,
						-sinl, 0, cosl ), ecl2gal );
	R = mul (ecl2gal, R);

	D3DXMatrixIdentity( &trans );
	trans._11 = float(R.m11);
	trans._12 = float(R.m12);
	trans._13 = float(R.m13);
	trans._21 = float(R.m21);
	trans._22 = float(R.m22);
	trans._23 = float(R.m23);
	trans._31 = float(R.m31);
	trans._32 = float(R.m32);
	trans._33 = float(R.m33);
}

CelBackground::~CelBackground() {
	if( disabled )
		return;

	if( ntex ) {
		for( DWORD j = 0; j < ntex; j++ )
		{
			REL( texbuf[j] );
		}
		delete [ ] texbuf;
	}
	delete [ ] tiledesc;
}

void CelBackground::SaveParams( int level, int bglvl ) {
	if( disabled )
		return;

	intens = Intensity;

	if( bglvl )
		intens *= exp( -(float)bglvl*0.05f );

	if( !intens )
		return;

	RP.tgtlvl = level = min( level, maxlvl );
	RP.mWorld = trans;
	double Aspect = (double)cfg->cWidth/(double)cfg->cHeight;//Scene function !
	RP.viewap = atan( Aspect*tan(oapiCameraAperture()) );

	MATRIX3 rcam;
	oapiCameraRotationMatrix( &rcam );
	rcam = mul( ecl2gal, rcam );
	RP.CamDir = _V( rcam.m13, rcam.m23, rcam.m33 );
}

void CelBackground::Render( /*int level, int bglvl*/ ) {
	if( disabled )
		return;

	const float BlendFactors[4] = { 1.0f };
	/*
	if( disabled )
		return;

	float intens = Intensity;

	if( bglvl )
		intens *= exp( -(float)bglvl*0.05f );

	if( !intens )
		return;

	D3DXCOLOR Color( intens, intens, intens, intens );
	dCtx->UpdateSubresource( cb_PS_CelBackground, 0, NULL, &Color, 0, 0 );
	dCtx->PSSetConstantBuffers( 1, 1, &cb_PS_CelBackground );

	RP.tgtlvl = level = min( (DWORD)level, maxlvl );
	RP.mWorld = trans;
	double Aspect = (double)cfg->cWidth/(double)cfg->cHeight;//Scene function !
	RP.viewap = atan( Aspect*tan(oapiCameraAperture()) );

	MATRIX3 rcam;
	oapiCameraRotationMatrix( &rcam );
	rcam = mul( ecl2gal, rcam );
	RP.CamDir = _V( rcam.m13, rcam.m23, rcam.m33 );
	*/
	int startlvl = maxlvl;
	int hemisphere, ilat, ilng, idx;
	int nlat = NLAT[startlvl];
	int *nlng = NLNG[startlvl];
	int texofs = patchidx[startlvl-1];

	TILEDESC *td = tiledesc + texofs;
	TEXCRDRANGE range = { 0, 1, 0, 1 };

	D3DXCOLOR Color( 3 * intens, 3 * intens, 3 * intens, intens );
	dCtx->UpdateSubresource( cb_PS_CelBackground, 0, NULL, &Color, 0, 0 );
	dCtx->PSSetConstantBuffers( 1, 1, &cb_PS_CelBackground );

	dCtx->IASetInputLayout( IL_TileVertex );
	dCtx->IASetPrimitiveTopology( D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST );
	dCtx->VSSetShader( VS_CelBackground, NULL, NULL );
	dCtx->PSSetShader( PS_CelBackground, NULL, NULL );
	dCtx->PSSetSamplers( 0, 1, &SS_CelBackground );

	dCtx->OMSetBlendState( BS_SrcAlpha_Blend_0, BlendFactors, 0xFFFFFFFF );
	dCtx->RSSetState( RS_Front_Solid );

	for( hemisphere = idx = 0; hemisphere < 2; hemisphere++ ) {
		if( hemisphere )
			D3DXMatrixMultiply( &RP.mWorld, &TileManager::RSouth, &RP.mWorld );
		for( ilat = nlat-1; ilat >= 0; ilat-- ) {
			for( ilng = 0; ilng < nlng[ilat]; ilng++ ) {
				ProcessTile( hemisphere, startlvl, ilat, nlat, ilng, nlng[ilat], td+idx,
					range, td[idx].tex );
				idx++;
			}
		}
	}
	dCtx->RSSetState( RS_Back_Solid );
}

void CelBackground::ProcessTile( int hemisphere, int lvl, int ilat, int nlat, int ilng, int nlng, TILEDESC *tile,
		const TEXCRDRANGE &range, ID3D11ShaderResourceView *tex )
{
	static const double rad0 = sqrt(2.0)*PI05;
	VECTOR3 cnt = GetTileCenter( hemisphere, ilat, nlat, ilng, nlng);
	double rad = rad0/(double)nlat;
	double adist = acos( dotp( RP.CamDir, cnt ) ) - rad;
	if( adist > RP.viewap )
		return;

	SetWorldMatrix( ilat, nlat, ilng, nlng );

	if( !IsTileInView( lvl, ilat ) )
		return;

	RenderTile( hemisphere, lvl, ilat, nlat, ilng, nlng, tile, range, tex );
}

void CelBackground::RenderTile( int hemisphere, int lvl, int ilat, int nlat, int ilng, int nlng,
		TILEDESC *tile, const TEXCRDRANGE &range, ID3D11ShaderResourceView *tex )
{
	const UINT TileVertexStride = 40, VBOffset = 0;
	TILEMESH &mesh = TPL[lvl][ilat];

	mWorld *= *SC->GetVP();
	dCtx->UpdateSubresource( cb_VS_CelBackground, 0, NULL, &mWorld, 0, 0 );
	dCtx->VSSetConstantBuffers( 0, 1, &cb_VS_CelBackground );
	dCtx->PSSetShaderResources( 0, 1, &tex );
	dCtx->IASetVertexBuffers( 0, 1, &mesh.VB, &TileVertexStride, &VBOffset );
	dCtx->IASetIndexBuffer( mesh.IB, DXGI_FORMAT_R16_UINT, 0 );

	dCtx->DrawIndexed( mesh.nidx, 0, 0 );
}

VECTOR3 CelBackground::GetTileCenter( int hemisphere, int ilat, int nlat, int ilng, int nlng ) {
	double cntlat = PI05*( (double)ilat + 0.5 )/(double)nlat,		slat = sin( cntlat ), clat = cos( cntlat );
	double cntlng = PI2*( (double)ilng + 0.5 )/(double)nlng + PI,	slng = sin( cntlng ), clng = cos( cntlng );
	if( hemisphere )	return _V( clat*clng, -slat, -clat*slng );
	else				return _V( clat*clng,  slat,  clat*slng );
}

void CelBackground::SetWorldMatrix( int ilat, int nlat, int ilng, int nlng ) {
	D3DXMATRIX rtile;

	double lng = PI2*(double)ilng/(double)nlng + PI;
	D3DMAT_RotY( &rtile, lng );
	D3DXMatrixMultiply( &mWorld, &rtile, &RP.mWorld );
}

bool CelBackground::IsTileInView( int lvl, int ilat ) {
	TILEMESH &mesh = TPL[lvl][ilat];

	float rad = mesh.bsRad*2000.0f;
	D3DXVECTOR3 vP;
	D3DXVec3TransformCoord( &vP, &mesh.bsCnt, &mWorld );
	return SC->IsVisibleInCamera( &vP, rad );
}

void CelBackground::LoadTextures() {
	char cpath[256];
	int j;
	for( j = 0; j < patchidx[maxbaselvl]; j++ )
		tiledesc[j].flag = 1;

	char fname[256];
	strcpy( fname, texname );
	strcat( fname, ".tex" );
	if( !gc->TexturePath( fname, cpath ) ) {
		texbuf = NULL;
		return;
	}

	ntex = patchidx[maxbaselvl];
	texbuf = new ID3D11ShaderResourceView *[ntex];

	if( ntex = tbuf->LoadTextures( cpath, ntex, 0, texbuf ) ) {
		while( (int)ntex < patchidx[maxbaselvl] )
			maxlvl = --maxbaselvl;
		while( (int)ntex > patchidx[maxbaselvl] )
			texbuf[--ntex]->Release();
		// not enough textures loaded for requested resolution level
		for( j = 0; j < patchidx[maxbaselvl]; j++ )
			tiledesc[j].tex = texbuf[j];
	}
	else {
		delete [ ] texbuf;
		texbuf = NULL;
	}
}